// pcaptest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include "ieee1722.h"

#include "pcap.h"

#include "VideoFrameView.h"


#define SAVE_H264_BITSTREAM

static char g_debuglog[100];


//
// Decoder
//
extern bool DecoderDecodeBitstream(void* pParam);

//
// Drawer
//
void DrawerDrawFrame(void* pParam = NULL);

static int memmem(unsigned char* pdata, int datasize, unsigned char* pneedle, int needlesize);

FILE* pf = NULL;

static void ProcessH264BitStream(unsigned char* p, int len, void* pParam = NULL);

/* prototype of the packet handler */
void packet_handler_ieee1722(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
void packet_handler_raw264(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

int pcaptest_main(void)
{
	pcap_if_t *alldevs;
	pcap_if_t *d;
	int inum;

	int i=0;
	pcap_t *adhandle;
	char errbuf[PCAP_ERRBUF_SIZE];
    
	errno_t err;

	/* Retrieve the device list on the local machine */
	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
	{
		fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
		exit(1);
	}
    
	/* Print the list */
	for(d=alldevs; d; d=d->next)
	{
		printf("%d. %s", ++i, d->name);
		if (d->description)
			printf(" (%s)\n", d->description);
		else
			printf(" (No description available)\n");
	}
    
	if(i==0)
	{
		printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
		return -1;
	}
    
//	printf("Enter the interface number (1-%d):",i);
//	scanf_s("%d", &inum);
	inum = 2;
    
	if(inum < 1 || inum > i)
	{
		printf("\nInterface number out of range.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}
    
	/* Jump to the selected adapter */
	for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
    
	/* Open the device */
	if ( (adhandle= pcap_open(d->name,          // name of the device
								65536,            // portion of the packet to capture
												// 65536 guarantees that the whole packet will be captured on all the link layers
								PCAP_OPENFLAG_PROMISCUOUS,    // promiscuous mode
								1000,             // read timeout
								NULL,             // authentication on the remote machine
								errbuf            // error buffer
								) ) == NULL)
	{
		fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}
    
	printf("\nlistening on %s...\n", d->description);
    
	/* At this point, we don't need any more the device list. Free it */
	pcap_freealldevs(alldevs);

#ifdef SAVE_H264_BITSTREAM
	err = fopen_s(&pf, "F:\\capture_0.264", "wb");
	if( err != 0) {
		fprintf(stderr, "\n Failed to open a .264 file.\n ");
		return 0;
	}
#endif
    
	/* start the capture */
	pcap_loop(adhandle, 0, packet_handler_ieee1722, NULL);
#if 0
	pcap_loop(adhandle, 0, packet_handler_raw264, NULL);
#endif

#ifdef SAVE_H264_BITSTREAM
	fclose(pf);
#endif
    
	return 0;
}


/* Callback function invoked by libpcap for every incoming packet */
void packet_handler_ieee1722(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	struct tm ltime;
	char timestr[16];
	time_t local_tv_sec;

	/*
		* unused variables
	*/
	(VOID)(param);
	(VOID)(pkt_data);

	/* convert the timestamp to readable format */
	local_tv_sec = header->ts.tv_sec;
	localtime_s(&ltime, &local_tv_sec);
	strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);
    
	printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);


	EthernetFrameHeader*	pethheader = NULL;
	IEEE1722Header*			pieee1722header = NULL;
	unsigned char*			ppayload = NULL;

	pethheader = (EthernetFrameHeader*)pkt_data;

	if (pethheader->type[0] == 0x22
		&& pethheader->type[1] == 0xF0)
	{
		int	stream_data_length = 0;

		printf("------------ IEEE1722 frame found\n");
		
		pieee1722header = (IEEE1722Header*)(pkt_data + sizeof(EthernetFrameHeader));
		stream_data_length = ( (int)(pieee1722header->stream_data_length[0]) << 8 )
							+ (int)(pieee1722header->stream_data_length[1]);

		ppayload = (unsigned char*)pieee1722header + sizeof(IEEE1722Header);

		printf("[%02X] [%02X] [%02X] [%02X]\n", (int)ppayload[0], (int)ppayload[1], (int)ppayload[2], (int)ppayload[3]);

		printf("stream_data_length : %d\n", stream_data_length);

		{
#ifdef SAVE_H264_BITSTREAM
			fwrite(ppayload, stream_data_length, 1, pf);
			fflush(pf);
#endif
			ProcessH264BitStream(ppayload, stream_data_length);
		}
	}
}



#if 0

/* Callback function invoked by libpcap for every incoming packet */
void packet_handler_raw264(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	struct tm ltime;
	char timestr[16];
	time_t local_tv_sec;

	/*
		* unused variables
	*/
	(VOID)(param);
	(VOID)(pkt_data);

#if 0
	/* convert the timestamp to readable format */
	local_tv_sec = header->ts.tv_sec;
	localtime_s(&ltime, &local_tv_sec);
	strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);
    
	printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
#endif


	EthernetFrameHeader*	pethheader = NULL;
	Raw264Header*			praw264header = NULL;
	unsigned char*			ppayload = NULL;

	pethheader = (EthernetFrameHeader*)pkt_data;

	if (pethheader->type[0] == 0x22
		&& pethheader->type[1] == 0xF0)
	{
		int	stream_data_length = 0;

		printf("------------ IEEE1722 frame found\n");
		
		praw264header = (Raw264Header*)(pkt_data + sizeof(EthernetFrameHeader));
		stream_data_length = ( (int)(praw264header->stream_data_length[0]) << 8 )
							+ (int)(praw264header->stream_data_length[1]);

		ppayload = (unsigned char*)praw264header + sizeof(Raw264Header);

		sprintf(g_debuglog, "[%02X] [%02X] [%02X] [%02X]\n", (int)ppayload[0], (int)ppayload[1], (int)ppayload[2], (int)ppayload[3]);
		OutputDebugStringA(g_debuglog);

		sprintf(g_debuglog, "stream_data_length : %d\n", stream_data_length);
		OutputDebugStringA(g_debuglog);

//#if 0
		{
#ifdef SAVE_H264_BITSTREAM
			fwrite(ppayload, stream_data_length, 1, pf);
			fflush(pf);
#endif
			ProcessH264BitStream(ppayload, stream_data_length);
		}
//#endif
	}
}

#endif




void filetest_main(void* pParam)
{
	VideoFrameView* pVFV = (VideoFrameView*) pParam;
	FILE*	pf = NULL;
	int		readcount = 0;

	unsigned char tempbuffer[992] = { 0, };
	
	errno_t err;
	
	err = fopen_s(&pf, pVFV->m_pFileName, "rb");
	if ( err != 0) {
		fprintf(stderr, "\n Failed to open a .264 file.\n ");
		return;
	}

	while(1)
	{
		readcount = fread(tempbuffer, 992, 1, pf);
		if (readcount != 1)
			break;
		ProcessH264BitStream(tempbuffer, 992, (void *) pVFV);
		Sleep(1000/30);
	}

	fclose(pf);
}



//
// buffer
//

static unsigned char needle[] = { 0x00, 0x00, 0x00, 0x01, 0x67 };


static void PutToBuffer(void* pParam, unsigned char* p, int len)
{
	VideoFrameView* pVFV = (VideoFrameView *)pParam;

	memcpy(pVFV->m_pcapCapture.pBufferH264 + pVFV->m_pcapCapture.H264Len, p, len);
	pVFV->m_pcapCapture.H264Len += len;
}

static bool HaveFullFrameBits(void* pParam)
{
	VideoFrameView* pVFV = (VideoFrameView *)pParam;

	int needlesize = sizeof(needle) / sizeof(needle[0]);
	int ret = 0;


	ret = memmem(pVFV->m_pcapCapture.pBufferH264+needlesize, pVFV->m_pcapCapture.H264Len, needle, needlesize);

	if (ret >= 0)
	{
		pVFV->m_pcapCapture.SecondFramePos = ret + needlesize;
		return true;
	}
	else
		return false;
}

static void RemoveFirstFrameBits(void* pParam)
{
	VideoFrameView * pVFV = (VideoFrameView *)pParam;
	
	memmove(pVFV->m_pcapCapture.pBufferH264, pVFV->m_pcapCapture.pBufferH264 + pVFV->m_pcapCapture.SecondFramePos, pVFV->m_pcapCapture.H264Len - pVFV->m_pcapCapture.SecondFramePos);
	pVFV->m_pcapCapture.H264Len -= pVFV->m_pcapCapture.SecondFramePos;
	pVFV->m_pcapCapture.SecondFramePos = 0;
}

#if 0	/* jhbae */
static bool DecodeFrame(void)
{
	return DecoderDecodeBitstream(g_pBufferH264, g_SecondFramePos);
}


static void DisplayFrame(void)
{
	DrawerDrawFrame();
}
#endif

static void ProcessH264BitStream(unsigned char* p, int len, void* pParam)
{
	PutToBuffer(pParam, p, len);

	if (HaveFullFrameBits(pParam))
	{
		if ( DecoderDecodeBitstream(pParam) )
		{
			DrawerDrawFrame(pParam);
		}

		RemoveFirstFrameBits(pParam);
	}
}


static int memmem(unsigned char* pdata, int datasize, unsigned char* pneedle, int needlesize)
{
	unsigned int needlesum = 0;
	unsigned int datasum = 0;

	for (int i = 0; i < needlesize; i++)
		needlesum += pneedle[i];

	for (int i = 0; i < needlesize; i++)
		datasum += pdata[i];

	for (int i = 0; i <= datasize-needlesize; i++)
	{
		if (datasum == needlesum)
		{
			// half found
			if (memcmp(pdata + i, pneedle, needlesize) == NULL)
			{
				// found
				return i;
			}
		}

		datasum -= pdata[i];
		datasum += pdata[i + needlesize];
	}


	return -1;
}
